var planner = (function () {
  var DEBUG = true;

  var console;
  if (DEBUG && window.console) {
    console = window.console;
  } else {
    console = { 'log' : function(msg) { } };
  }

  var _cloneLatLngBounds = function (bounds) {
    var sw = bounds.getSouthWest();
    var ne = bounds.getNorthEast();

    return new google.maps.LatLngBounds(
      new google.maps.LatLng(sw.lat(), sw.lng()),
      new google.maps.LatLng(ne.lat(), ne.lng()));
  };


  // -- SpotSelector --------------------

  var SpotSelector = function (canvasJquery, countJquery, outputJquery,
                               listJquery, homeKey, homeName, lat, lon, zoom) {
    if (canvasJquery == null || canvasJquery == undefined ||
        countJquery == null || countJquery == undefined) {
      throw "Invalid constructor argument";
    }

    this.canvasJquery = canvasJquery;
    this.countJquery = countJquery;
    this.outputJquery = outputJquery;
    this.listJquery = listJquery;
    this.homeKey = homeKey;

    var mapOptions = {
      'center': new google.maps.LatLng(lat, lon),
      'zoom': zoom,
      'mapTypeId': google.maps.MapTypeId.ROADMAP,
    };

    this.map = new google.maps.Map(canvasJquery[0], mapOptions);
    this.infoWindow = new google.maps.InfoWindow();
    this.selectedSpotsCount = 0;
    this.selectedSpots = new Object();

    this.availCategoryList = new Object();
    this.availSpotsItem = new Object();
    this.availMarkers = new Object();
    this.availBounds = null;

    this.updateSpotsCount();
    this.updateSelectedSpotsOutput();
    this.focusMarker(homeKey, homeName, lat, lon);
    this.loadSpotsList();

    var this_ = this;

    google.maps.event.addListener(this.map, 'bounds_changed', function (e) {
      var bounds = this_.map.getBounds();
      var ne = bounds.getNorthEast();
      var sw = bounds.getSouthWest();

      var new_bounds;

      if (this_.availBounds === null) {
        new_bounds = bounds;
      } else {
        new_bounds = _cloneLatLngBounds(this_.availBounds);
        new_bounds.union(bounds);

        if (this_.availBounds.equals(new_bounds)) {
          console.log('[MAP] Map bounds has been covered.');
          return;
        }
      }

      $.ajax({
        url: 'query_spots_in_bound',

        dataType: 'json',

        data: {
          'ne_lat': ne.lat(),
          'ne_lon': ne.lng(),
          'sw_lat': sw.lat(),
          'sw_lon': sw.lng()
        },

        success: function (data) {
          var i, spots;

          if ('spots' in data) {
            spots = data.spots;
          } else {
            var msg = data.msg || '';
            console.log('[QUERY] ERROR: ' + msg);
            return;
          }

          console.log('[QUERY] GOT: ' + spots.length + ' spots.');

          for (i = 0; i < spots.length; ++i) {
            this_.addMarker(spots[i].key, spots[i].name,
                            spots[i].lat, spots[i].lon);
          }

          this_.availBounds = new_bounds;
        }
      });
    });
  };

  var ssp = SpotSelector.prototype;

  var _renderInfoWindow = function (this_, key, name, lat, lon) {
    var all = $('<div></div>');

    // Scenic spot name
    var h3 = $('<h3></h3>');

    if (key == this_.homeKey) {
      h3.html(name + ' (起點)');
      h3.addClass('SelectedSpot');
    } else if (key in this_.selectedSpots) {
      h3.html(name + ' (己選)');
      h3.addClass('SelectedSpot');
    } else {
      h3.html(name);
    }

    all.append(h3);

    // Add or delete button
    if (key !== this_.homeKey) {
      var form = $('<form onsubmit="return false;"></form>');
      var btn = $('<input type="button" />');

      if (key in this_.selectedSpots) {
        btn.val('刪除');
        btn.click(function (e) {
          this_.deleteSpot(key);
          _renderInfoWindow(this_, key, name, lat, lon);
        });
      } else {
        btn.val('加入');
        btn.click(function (e) {
          this_.addSpot(key);
          _renderInfoWindow(this_, key, name, lat, lon);
        });
      }

      form.append(btn);
      form.submit(function (e) {
        return false;
      });

      all.append(form);
    }

    // Update the info window
    this_.infoWindow.setContent(all[0]);
  };

  var _showInfoWindow = function (this_, key, name, lat, lon, pos, marker) {
    _renderInfoWindow(this_, key, name, lat, lon);
    this_.infoWindow.setPosition(pos);
    this_.infoWindow.open(this_.map, marker);
  };

  ssp.addMarker = function (key, name, lat, lon) {
    if (key in this.availMarkers) {
      return this.availMarkers[key];
    }

    var pos = new google.maps.LatLng(lat, lon);

    var marker = new google.maps.Marker({
      position: pos,
      map: this.map,
      title: name
    });

    var this_ = this;

    google.maps.event.addListener(marker, 'click', function () {
      _showInfoWindow(this_, key, name, lat, lon, pos, marker);
    });

    var pair = { 'marker': marker , 'pos': pos };
    this.availMarkers[key] = pair;
    return pair;
  };

  ssp.focusMarker = function (key, name, lat, lon) {
    var pair = this.addMarker(key, name, lat, lon);
    //this.map.setCenter(pair.pos); // FIXME: Map will be moved twice.
    _showInfoWindow(this, key, name, lat, lon, pair.pos, pair.marker);
  };

  ssp.updateSpotsCount = function () {
    var this_ = this;

    var link = $('<a href="#"></a>');
    link.click(function (e) {
      this_.showSelectedSpots();
      return false;
    });

    if (this.selectedSpotsCount === 0) {
      link.html('您尚未選取任何景點');
    } else {
      link.html('您已經選了 <strong>' + this.selectedSpotsCount +
                '</strong> 個景點');
    }

    this.countJquery.html(link);
  };

  ssp.showSelectedSpots = function () {
    var i;
    var str = '';
    for (i in this.selectedSpots) {
      str += i + '\n';
    }
    alert(str);
  };

  ssp.addSpot = function (key) {
    if (key in this.selectedSpots) {
      console.log('[SELECTOR] Existed spot (Key: ' + key + ')');
    } else {
      console.log('[SELECTOR] Adding new spot (Key: ' + key + ')');
      this.selectedSpotsCount++;
      this.selectedSpots[key] = {};
      this.updateSpotsCount();
    }

    this.updateSelectedSpotsOutput();
  };

  ssp.deleteSpot = function (key) {
    if (key in this.selectedSpots) {
      console.log('[SELECTOR] Delete spot (Key: ' + key + ')');
      delete this.selectedSpots[key];
      this.selectedSpotsCount--;
      this.updateSpotsCount();
    }

    this.updateSelectedSpotsOutput();
  };

  ssp.updateSelectedSpotsOutput = function () {
    var key;
    var output = '';
    for (key in this.selectedSpots) {
      output += key + ';';
    }

    this.outputJquery.val(output);
  };

  var _createSpotItem = function (this_, spot) {
    var spotItem = $('<li></li>');
    var spotLink = $('<a href="#"></a>');

    spotLink.text(spot.name);
    spotLink.click(function (e) {
      this_.focusMarker(spot.key, spot.name, spot.lat, spot.lon);
      return false;
    });

    spotItem.append(spotLink);
    return spotItem;
  };

  ssp.addCategoryList = function (category) {
    var i;
    var this_ = this;
    var spotsList = $('<ul class="SpotsList"></ul>');

    // Add the spots to the list
    for (i = 0; i < category.top_spots.length; ++i) {
      var spot = category.top_spots[i];
      var spotItem = _createSpotItem(this, spot);
      spotsList.append(spotItem);
      this.availSpotsItem[spot.key] = spotItem;
    }

    // Add get more spots to the list
    if (category.spots_count > category.top_spots.length) {
      var moreItem = $('<li></li>');
      var moreLink = $('<a href="#" class="SpotsListMore">(更多)</a>');
      moreLink.click(function (e) {
        this_.loadMoreSpotsList(category.key);
        return false;
      });

      moreItem.append(moreLink);
      spotsList.append(moreItem);
    }

    // Register this category list
    this.availCategoryList[category.key] = spotsList;

    // Append to the list content dom tree
    this.listJquery.append($('<h2></h2>').text(category.name));
    this.listJquery.append(spotsList);
    this.listJquery.append($('<div class="Cleaner"></div>'));
  };

  ssp.loadSpotsList = function () {
    var this_ = this;

    this.listJquery.html('<p>載入中...</p>');

    $.ajax({
      url: 'query_spots_list',
      dataType: 'json',

      success: function (categories) {
        var i;

        // Remove every child in spot list content
        this_.listJquery.empty();

        // Show the categories
        for (i = 0; i < categories.length; ++i) {
          this_.addCategoryList(categories[i]);
        }
      },

      error: function (request, textStatus, error) {
        console.log('error' + error);

        var retry_paragraph = $('<p></p>');
        var retry = $('<a href="#">重試</a>');
        retry.click(function (e) {
          this_.loadSpotsList();
          return false;
        });

        this_.listJquery.html('<p>無法載入景點列表</p>');
        this_.listJquery.append(retry_paragraph.append(retry));
      },
    });
  };

  ssp.loadMoreSpotsList = function (categoryKey) {
    var this_ = this;

    if (!categoryKey in this.availCategoryList) {
      console.log('unavailable category list: ' + categoryKey);
      return;
    }

    $.ajax({
      url: 'query_spots_in_category',
      dataType: 'json',
      data: { 'category_key': categoryKey },

      success: function (spots) {
        var i, spot;
        var categoryList = this_.availCategoryList[categoryKey];

        console.log('[QUERY] GOT MORE: ' + spots.length + ' spots.');

        // Remove "MORE" link
        categoryList.children().last().remove();

        // Append the list
        for (i = 0; i < spots.length; ++i) {
          spot = spots[i];
          if (!(spot.key in this_.availSpotsItem)) {
            console.log('NEW: ' + spot.name);
            var spotItem = _createSpotItem(this_, spot);
            categoryList.append(spotItem);
            this_.availSpotsItem[spot.key] = spotItem;
          }
        }
      },
    });
  };


  // -- SpotReorder --------------------
  var SpotReorder = function (listJquery, outputJquery, formJquery) {
    this.listJquery = listJquery;
    this.outputJquery = outputJquery;
    this.formJquery = formJquery;

    listJquery.sortable();
    listJquery.disableSelection();

    formJquery.submit(function (e) {
      var output = '';
      listJquery.children().each(function (idx, item) {
        output += $(item).attr('id').substr(5) + ';';
      });
      console.log(output);
      outputJquery.val(output);
    });
  };


  // -- Public Interfaces --------------------
  return {
    'SpotSelector': SpotSelector,
    'SpotReorder': SpotReorder,
  };

})();
